package filehandler

import (
	"fmt"
	"gitee.com/mjun1833/buildnotification/utils/dgfile/loggo"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"
)

type cursorMap struct {
	sync.Mutex
	mp map[string]int64
}
type FileScanner struct {
	FileType                 string // 文件类型，
	BaseDir                  string // 扫描文件的路径
	BatchNum                 int    // 同时扫描的文件个数
	ByesToPrint              int64  // 每处理多少byte数据，打印一次统计信息，默认值:10000000
	RemoveHandledFile        bool   // 处理完文件之后是否删除
	handling                 bool
	statusLocked             bool
	cursorMap                cursorMap
	fileToHandle             []fileState
	fileName                 string
	FileValidation           func(info os.FileInfo) bool   // 文件验证，过滤不需要处理的文件
	FileNameValidation       func(name string) bool        // 文件验证，过滤不需要处理的文件
	FileNameSortFunc         func(names []string) []string // 文件名排序，非必须
	HandleByteFunc           func(line []byte) error       // 一行数据的处理方法 参数为[]byte类型
	HandleLineFunc           func(line string) error       // 同上，参数为string类型
	HandleByteFuncByFileName func(line []byte, filename []byte) error
	handlerChan              chan *fileHandler
	stateChan                chan fileState
	stateTicker              *time.Ticker
	stateChange              bool
	scanning                 bool
	runLog                   *loggo.Logger
}

func (p *FileScanner) statusChange(fileName string, cursor int64) {
	p.stateChange = true
	p.stateChan <- fileState{fileName: fileName, cursor: cursor}
}

func (p *FileScanner) initState() {
	p.stateTicker = time.NewTicker(5 * time.Second)
	p.stateChan = make(chan fileState, 10)
	go p.watchState()
	go p.stateWriter()
}

func (p *FileScanner) stateWriter() {
	for {
		select {
		case <-p.stateTicker.C:
			if p.stateChange {
				p.writeStatus(false)
				p.stateChange = false
			}
		}
	}
}
func (p *FileScanner) watchState() {
	for data := range p.stateChan {
		p.cursorMap.Lock()
		p.cursorMap.mp[data.fileName] = data.cursor
		p.cursorMap.Unlock()
	}
}
func (p *FileScanner) Stop() error {
	p.stateTicker.Stop()
	p.statusLocked = true
	p.writeStatus(true)
	return nil
}
func (p *FileScanner) Init() {
	if p.ByesToPrint == 0 {
		p.ByesToPrint = 100000
	}

	p.runLog = &loggo.Logger{}
	p.runLog.SetWriter(&loggo.FileWriter{
		FileName: fmt.Sprintf("./log/scanner-%s/scanner.log", p.FileType),
		Compress: true,
		MaxSize:  1000,
		MaxAge:   3,
	})

	p.initState()
}

// 循环扫描，可扫描新增文件
func (p *FileScanner) LoopScan(duration time.Duration) error {
	i := 1
	for {
		if p.scanning {
			p.runLog.Infoln("Scan running,wait " + duration.String())
			return nil
		}
		fmt.Printf("scan %v 次\n", i)
		p.scanning = true
		p.Scan()
		p.scanning = false
		time.Sleep(duration)
		err := RemoveDirAllFiles("./status")
		if err != nil {
			return err
		}
		i++
	}
}

func RemoveDirAllFiles(dir string) error {
	// 遍历文件夹下的所有文件和子文件夹
	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// 判断是否为文件，如果是文件则删除
		if !info.IsDir() && strings.Contains(info.Name(), "status_log") {
			err := os.Remove(path)
			if err != nil {
				return err
			}
		}

		return nil
	})

	return err
}

// 一次性扫描，扫完即停止
func (p *FileScanner) Scan() error {
	p.runLog.Infoln("scan ", p.BaseDir)
	if err := p.readStatus(); err != nil {
		p.runLog.Infoln("read cursorMap error", err.Error())
		return err
	}
	p.fileToHandle = []fileState{}
	for k := range p.cursorMap.mp {
		cursor := p.cursorMap.mp[k]
		if cursor == -1 {
			continue
		}
		p.fileToHandle = append(p.fileToHandle, fileState{fileName: k, cursor: cursor})
	}

	err := filepath.Walk(p.BaseDir,
		func(fileName string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if info.IsDir() {
				return nil
			}
			if p.FileNameValidation != nil && !p.FileNameValidation(fileName) {
				return nil
			}
			if p.FileValidation != nil && !p.FileValidation(info) {
				return nil
			}
			if _, ok := p.cursorMap.mp[fileName]; !ok {
				p.fileToHandle = append(p.fileToHandle, fileState{fileName: fileName, cursor: 0})
				p.cursorMap.mp[fileName] = 0
			}
			return nil
		})
	if err != nil {
		p.runLog.Errorln(err.Error())
	}

	// file name sort
	names := make([]string, len(p.fileToHandle))
	fm := make(map[string]int64)
	for k, v := range p.fileToHandle {
		names[k] = v.fileName
		fm[v.fileName] = v.cursor
	}
	if p.FileNameSortFunc != nil {
		names = p.FileNameSortFunc(names)
	} else {
		sort.Sort(sort.StringSlice(names))
	}
	for k, v := range names {
		p.fileToHandle[k] = fileState{fileName: v, cursor: fm[v]}
	}

	p.runLog.Infofn("fileToHandle:%+v", p.fileToHandle)
	p.handleFiles()
	return nil
}
func (p *FileScanner) handleFiles() error {
	if p.handling {
		return nil
	}
	p.handling = true
	defer func() {
		p.handling = false
	}()
	if p.BatchNum == 0 {
		p.BatchNum = 1
	}
	p.handlerChan = make(chan *fileHandler, p.BatchNum)
	for i := 0; i < p.BatchNum; i++ {
		handler := fileHandler{scanner: p, bytesToPrint: p.ByesToPrint}
		handler.initLogger(i)
		p.handlerChan <- &handler
	}
	wg := sync.WaitGroup{}
	wg.Add(len(p.fileToHandle))
	for _, e := range p.fileToHandle {
		handler := <-p.handlerChan
		go handler.HandleFile(p.RemoveHandledFile, e.fileName, e.cursor, p.handlerChan, &wg)
	}
	wg.Wait()
	p.runLog.Infoln("------All files handle finish")
	return nil
}
